package mrs.ontology;

import java.net.URI;
import java.util.*;

import org.mindswap.pellet.owlapi.Reasoner;
import org.semanticweb.owl.apibinding.OWLManager;
import org.semanticweb.owl.model.*;

import mrs.models.*;
import org.grlea.log.SimpleLogger;

/**
 * @author Yo mismo
 */
public class FoodOntology {

  // Logging
  private static final SimpleLogger log = new SimpleLogger(FoodOntology.class);
  
  // Ontology location and namespace
  
  public static final String MENU_FILE = "file:/" + System.getProperty("user.dir").replace('\\', '/').replaceAll(" ", "%20") + "/data/ontology/menus.owl";
  public static final String MENU_URI = "http://www.owl-ontologies.com/menus.owl";
  
  private static OWLOntologyManager manager;
  private static OWLOntology ontology;
  private static OWLDataFactory factory;
  private static Reasoner reasoner;
  

  static {

    FoodOntology.reloadFreshOntology();
  
  }

  private static void reloadFreshOntology()
  {
      manager = OWLManager.createOWLOntologyManager();

    try {

      ontology = manager.loadOntology(URI.create(MENU_FILE));
      log.info("Ontology loaded: " + MENU_FILE);

    } catch (Exception ex) {

      log.fatal(ex.toString());
      System.exit(1);
      
    }

    factory = manager.getOWLDataFactory();
    reasoner = new Reasoner(manager);
    
  }
  

  // Case 1: Recipes you can prepare using SOME of your available ingredients.
  //         You might also need to buy some MORE.
  //
  //    RecipesWithTomatoOrPepper <=>
  //              Recipe and
  //              hasIngredient some Tomato or
  //              hasIngredient some Pepper
  //
  public static Vector<Recipe> RecipesSomeIngredientsBuyMore(Set<Ingredient> ingredients) {

    log.info("RecipesSomeIngredientsBuyMore START");
    
    // Obtain a fresh environment
    FoodOntology.reloadFreshOntology();

    // Create new dummy recipe class
    OWLClass dummyRecipe = factory.getOWLClass(URI.create(MENU_URI + "#RecipeSomeIngredientsBuyMore"));

    // Create set to add descriptions
    Set orSet = new HashSet();

    // Build the union of all ingredients
    for (Ingredient ingredient : ingredients) {
      orSet.add(factory.getOWLObjectSomeRestriction(factory.getOWLObjectProperty(URI.create(MENU_URI + "#hasIngredient")),
              factory.getOWLClass(URI.create(MENU_URI + "#" + ingredient.name))));
    }

    Set andSet = new HashSet();

    // Build intersection of Recipe and (hasIngredient X or hasIngredient Y ...)
    andSet.add(factory.getOWLClass(URI.create(MENU_URI + "#Recipe")));
    andSet.add(factory.getOWLObjectUnionOf(orSet));

    // Build an axiom for necessary and sufficient conditions for DummyRecipe

    OWLAxiom dummyRecipeAxiom = factory.getOWLEquivalentClassesAxiom(dummyRecipe,
            factory.getOWLObjectIntersectionOf(andSet));
    try {

      // Add the axiom to the ontology
      manager.addAxiom(ontology, dummyRecipeAxiom);

    } catch (Exception ex) {

      log.error(ex.toString());
      System.exit(1);

    }

//    try {
//
//      manager.saveOntology(ontology); //FIXME: Delete
//
//    } catch (Exception ex) {
//
//      log.error(ex.toString());
//      System.exit(1);
//
//    }

    reasoner.loadOntology(ontology);

    Vector<Recipe> recipeList = new Vector<Recipe>();

    for (Set<OWLClass> owlSet : reasoner.getSubClasses(dummyRecipe)) {
      for (OWLClass owlClass : owlSet) {
        Recipe r = new Recipe(owlClass.toString());
        recipeList.add(r);
        log.info("ADDED: " + r.toString());
      }
    }

    log.info("RecipesSomeIngredientsBuyMore END");

    return (recipeList);
  }
  // Case 2: Recipes you can prepare using ALL your available ingredients.
  //         You might also need to buy some MORE.

  public static Vector<Recipe> RecipesAllIngredientsBuyMore(Set<Ingredient> ingredients) {

    log.info("RecipesAllIngredientsBuyMore START");
    
    // Obtain a fresh environment
    FoodOntology.reloadFreshOntology();
    
    // Create new dummy recipe class
    OWLClass dummyRecipe = factory.getOWLClass(URI.create(MENU_URI + "#RecipeAllIngredientsBuyMore"));

    // Create set to add descriptions
    Set andSet = new HashSet();

    // Build intersection of Recipe and hasIngredient X and hasIngredient Y...
    andSet.add(factory.getOWLClass(URI.create(MENU_URI + "#Recipe")));

    for (Ingredient ingredient : ingredients) {
      andSet.add(factory.getOWLObjectSomeRestriction(factory.getOWLObjectProperty(URI.create(MENU_URI + "#hasIngredient")),
              factory.getOWLClass(URI.create(MENU_URI + "#" + ingredient.name))));
    }

    // Build an axiom for the dummy recipe 

    OWLAxiom dummyRecipeAxiom = factory.getOWLEquivalentClassesAxiom(dummyRecipe,
            factory.getOWLObjectIntersectionOf(andSet));
    try {

      // Add the axiom to the ontology
      manager.addAxiom(ontology, dummyRecipeAxiom);

    } catch (OWLOntologyChangeException ex) {
      System.out.println(ex);
      System.exit(1);
    }

//    try {
//
//      manager.saveOntology(ontology); //FIXME: Delete
//    } catch (OWLOntologyStorageException ex) {
//      System.out.println(ex);
//      System.exit(1);
//    } catch (UnknownOWLOntologyException ex) {
//      System.out.println(ex);
//      System.exit(1);
//    }

    reasoner.loadOntology(ontology);

    Vector<Recipe> recipeList = new Vector<Recipe>();

    for (Set<OWLClass> owlSet : reasoner.getSubClasses(dummyRecipe)) {
      for (OWLClass owlClass : owlSet) {
        recipeList.add(new Recipe(owlClass.toString()));
      }
    }

    log.info("RecipesAllIngredientsBuyMore END");
        
    return (recipeList);
  }
  // Case 3: Recipes you can prepare with SOME of your available ingredientes.
  //         You don't need to buy any more.
  //    * e.g. RecipesWithOnlyChickenorTomatoOrPepper <=>
  //              Recipe and
  //              hasIngredient only (Chicken or Tomato or Pepper)
  //              and hasIngredient max 2
  //  
  public static Vector<Recipe> RecipesSomeIngredientsNoMore(Set<Ingredient> ingredients) {

    log.info("RecipesSomeIngredientsNoMore START");
    
    // Obtain a fresh environment
    FoodOntology.reloadFreshOntology();
    
    // Create new dummy recipe class
    OWLClass dummyRecipe = factory.getOWLClass(URI.create(MENU_URI + "#RecipeSomeIngredientsNoMore"));


    // Create set to add descriptions
    Set orSet = new HashSet();

    // Build the union of all ingredients
    for (Ingredient ingredient : ingredients) {
      orSet.add(factory.getOWLClass(URI.create(MENU_URI + "#" + ingredient.name)));
    }

    Set andSet = new HashSet();

    andSet.add(factory.getOWLClass(URI.create(MENU_URI + "#Recipe")));
    andSet.add(factory.getOWLObjectAllRestriction(factory.getOWLObjectProperty(URI.create(MENU_URI + "#hasIngredient")), factory.getOWLObjectUnionOf(orSet)));
    andSet.add(factory.getOWLObjectMaxCardinalityRestriction(factory.getOWLObjectProperty(URI.create(MENU_URI + "#hasIngredient")), ingredients.size() - 1));

    OWLAxiom dummyRecipeAxiom = factory.getOWLEquivalentClassesAxiom(dummyRecipe,
            factory.getOWLObjectIntersectionOf(andSet));
    try {

      // Add the axiom to the ontology
      manager.addAxiom(ontology, dummyRecipeAxiom);

    } catch (OWLOntologyChangeException ex) {
      System.out.println(ex);
      System.exit(1);
    }

//    try {
//
//      manager.saveOntology(ontology); //FIXME: Delete
//    } catch (OWLOntologyStorageException ex) {
//      System.out.println(ex);
//      System.exit(1);
//    } catch (UnknownOWLOntologyException ex) {
//      System.out.println(ex);
//      System.exit(1);
//    }

    reasoner.loadOntology(ontology);
    
    Vector<Recipe> recipeList = new Vector<Recipe>();

    for (Set<OWLClass> owlSet : reasoner.getSubClasses(dummyRecipe)) {
      for (OWLClass owlClass : owlSet) {
        recipeList.add(new Recipe(owlClass.toString()));
      }
    }
    
    log.info("RecipesSomeIngredientsNoMore END");

    return (recipeList);

  }
  // Case 4: Recipes you can prepare using ALL your available ingredients.
  //         You don't need to buy any more.
  //    * e.g. RecipesWithOnlyChickenAndTomato <=>
  //              Recipe and
  //              hasIngredient some Chicken and
  //              hasIngredient some Tomato and
  //              hasIngredient exactly 2
  //
  public static Vector<Recipe> RecipesAllIngredientsNoMore(Set<Ingredient> ingredients) {

    log.info("RecipesAllIngredientsNoMore START");
    
    // Obtain a fresh environment
    FoodOntology.reloadFreshOntology();
    
    // Create new dummy recipe class
    OWLClass dummyRecipe = factory.getOWLClass(URI.create(MENU_URI + "#RecipeAllIngredientsNoMore"));

    // Create set to add descriptions
    Set andSet = new HashSet();

    // Build intersection of Recipe and hasIngredient X and hasIngredient Y...
    andSet.add(factory.getOWLClass(URI.create(MENU_URI + "#Recipe")));

    for (Ingredient ingredient : ingredients) {
      andSet.add(factory.getOWLObjectSomeRestriction(factory.getOWLObjectProperty(URI.create(MENU_URI + "#hasIngredient")),
              factory.getOWLClass(URI.create(MENU_URI + "#" + ingredient.name))));
    }

    andSet.add(factory.getOWLObjectExactCardinalityRestriction(factory.getOWLObjectProperty(URI.create(MENU_URI + "#hasIngredient")), ingredients.size()));


    // Build an axiom for the dummy recipe 

    OWLAxiom dummyRecipeAxiom = factory.getOWLEquivalentClassesAxiom(dummyRecipe,
            factory.getOWLObjectIntersectionOf(andSet));
    try {

      // Add the axiom to the ontology
      manager.addAxiom(ontology, dummyRecipeAxiom);

    } catch (OWLOntologyChangeException ex) {
      System.out.println(ex);
      System.exit(1);
    }

//    try {
//
//      manager.saveOntology(ontology); //FIXME: Delete
//    } catch (OWLOntologyStorageException ex) {
//      System.out.println(ex);
//      System.exit(1);
//    } catch (UnknownOWLOntologyException ex) {
//      System.out.println(ex);
//      System.exit(1);
//    }

    reasoner.loadOntology(ontology);
    
    Vector<Recipe> recipeList = new Vector<Recipe>();

    for (OWLClass owlClass : reasoner.getEquivalentClasses(dummyRecipe)) {
        recipeList.add(new Recipe(owlClass.toString()));
    }

    log.info("RecipesAllIngredientsNoMore END");
    
    return (recipeList);
  }
}
